package net.luohuasheng.bee.proxy.mybatis.executor.processor;

import net.luohuasheng.bee.jdbc.tools.common.utils.ReflectionUtils;
import net.luohuasheng.bee.proxy.core.utils.StringUtils;
import net.luohuasheng.bee.proxy.core.utils.convert.ValueConvertUtils;
import net.luohuasheng.bee.proxy.core.utils.jpa.annotation.TableLogic;
import net.luohuasheng.bee.proxy.core.utils.jpa.dto.JpaColumnDto;
import net.luohuasheng.bee.proxy.mybatis.common.exception.MybatisException;
import net.luohuasheng.bee.proxy.mybatis.common.utils.MapperUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.jdbc.SQL;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;

import java.util.ArrayList;
import java.util.List;

import static net.luohuasheng.bee.proxy.mybatis.executor.intercetor.BaseInterceptor.LANGUAGE_DRIVER;

public abstract class BaseSaveOrUpdateProcessor extends BaseProcessor {
    public static final String SAVE_UPDATE = "SAVE UPDATE";

    public BaseSaveOrUpdateProcessor(Configuration configuration, Class<?> entityClass) {
        super(configuration, entityClass);
    }

    @Override
    public void rewrite(Invocation invocation, MappedStatement ms, BoundSql boundSql, Object parameter) throws Exception {
        long total = getTotal(invocation, ms, parameter);
        if (total == 0) {
            callLog(ms);
            insertValue(invocation, ms, parameter);
        } else if (total == 1) {
            callLog(ms);
            updateValue(invocation, ms, parameter);

        } else {
            throw new MybatisException("Operation more than 1 data is not allowed");
        }

    }

    protected abstract void updateValue(Invocation invocation, MappedStatement ms, Object parameter);

    protected abstract void insertValue(Invocation invocation, MappedStatement ms, Object parameter) throws Exception;

    protected SqlSource insertSqlSource(MappedStatement ms, Object parameter, String parent) throws Exception {
        List<JpaColumnDto> fields = new ArrayList<>();
        for (JpaColumnDto column : jpaTableDto.getColumns()) {
            column.getField().setAccessible(true);
            if (column.getTableLogic() != null) {
                TableLogic logic = column.getTableLogic();
                column.getField().set(parameter, ValueConvertUtils.convert(logic.noDelFlag(), column.getField().getType()));
            }
            Object value = column.getField().get(parameter);
            column.getField().setAccessible(false);
            if (column.isPk()) {
                fields.add(column);
            } else if (value instanceof String) {
                if (StringUtils.isNotEmpty(value.toString())) {
                    fields.add(column);
                }
            } else if (value != null) {
                fields.add(column);
            }
        }
        String[] fieldNames = new String[fields.size()];
        String[] columnNames = new String[fields.size()];
        for (int i = 0; i < fields.size(); i++) {
            JpaColumnDto column = fields.get(i);
            if (StringUtils.isEmpty(parent)) {
                fieldNames[i] = "#{" + column.getField().getName() + "}";
            } else {
                fieldNames[i] = "#{" + parent + "." + column.getField().getName() + "}";

            }
            columnNames[i] = column.getName();
        }
        SQL insertInto = new SQL().INSERT_INTO(jpaTableDto.getName());
        insertInto.INTO_COLUMNS(columnNames);
        insertInto.INTO_VALUES(fieldNames);
        String sql = insertInto.toString();
        return LANGUAGE_DRIVER.createSqlSource(ms.getConfiguration(), "<script>\n\t" + sql + "</script>", null);
    }

    protected abstract long getTotal(Invocation invocation, MappedStatement ms, Object parameter) throws Exception;

    protected long getTotal(Executor executor, MappedStatement ms, Object parameter, String sql) throws Exception {
        callLog(ms);
        SqlSource sqlSource = LANGUAGE_DRIVER.createSqlSource(ms.getConfiguration(), "<script>\n\t" + sql + "</script>", null);
        ms = MapperUtils.copyFromNewSql(ms, sqlSource.getBoundSql(parameter));
        ReflectionUtils.setFieldValue(ms, "keyGenerator", new NoKeyGenerator());
        long total = (long) executor.query(ms, parameter, RowBounds.DEFAULT, null).get(0);
        ReflectionUtils.setFieldValue(ms, "keyGenerator", keyGenerator());
        return total;
    }

}
